package com.sw.study.presisence.executor;

import com.sw.study.presisence.pojo.Configuration;
import com.sw.study.presisence.pojo.MappedStatement;
import com.sw.study.presisence.uti.GenericTokenParser;
import com.sw.study.presisence.uti.ParameterMapping;
import com.sw.study.presisence.uti.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by sw on 2020/3/25.
 * TODO 共有代码处 暂时不抽取
 */
public class DefaultExecutor implements Executor {
    /**
     *
     * @param configuration
     * @param mappedStatement
     * @param params 不是普通的参数  是一个pojo类的实例
     * @param <E>
     * @return
     * @throws SQLException
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        /**
         * 1.获取数据库连接信息
         */
        Connection connection = configuration.getDataSource().getConnection();
        /**
         * 2.转化sql  select* from tb_user where id=#{id} and name=#{username}
         *   select * from tb_user where id=? and name=?
         */
        String sql=mappedStatement.getSql();
        /**
         * 处理sql
         *
         */
        BoundSql boundSql=getBoundSql(sql);
        String finalSql=boundSql.getSqlText();
        /**
         * 获取参数
         */
        Class<?> paramType= getClassType( mappedStatement.getParamterType());
        PreparedStatement preparedStatement=connection.prepareStatement(finalSql);
        //解析后的参数属性集合
        List<ParameterMapping> parameterMappingList=boundSql.getParameterMappingList();
        int paramListSize=parameterMappingList.size();
        ParameterMapping parameterMapping=null;
        for(int i=0;i<paramListSize;i++){
            parameterMapping=parameterMappingList.get(i);
            /**
             * 反射得到类的属性信息
             */
            String propertyName=parameterMapping.getContent();
            Field declaredField = paramType.getDeclaredField(propertyName);
            /**
             * 暴力设置属性被访问
             */
            declaredField.setAccessible(true);
            /**
             * 参数值
             */
            Object o=declaredField.get(params[0]);
            preparedStatement.setObject(i+1,o);
        }
        /**
         * 执行sql
         */
        ResultSet resultSet= preparedStatement.executeQuery();
        Class resutTypeClass=getClassType(mappedStatement.getResutType());
        List<Object> result=new ArrayList<>();
        /**
         * 处理结果集合
         */
        while (resultSet.next()){
            /**
             * 获取列的原数据
             */
            Object o=resutTypeClass.newInstance();
            ResultSetMetaData metaData=resultSet.getMetaData();
            /**
             * 列长度
             */
            int columns= metaData.getColumnCount();
            for(int i=1;i<=columns;i++){
                /**
                 * 得到列名称和值信息
                 */
                String columnName=metaData.getColumnName(i);
                Object value=resultSet.getObject(columnName);
                /**
                 * 创建属性描述器，为属性生成读写方法
                 */
                PropertyDescriptor propertyDescriptor=new PropertyDescriptor(columnName,resutTypeClass);
                /**
                 * 获取写方法
                 */
                Method method=propertyDescriptor.getWriteMethod();
                method.invoke(o,value);
            }
            result.add(o);
        }
        return (List<E>) result;
    }



    @Override
    public int execute(Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception {
        /**
         * 1.获取数据库连接信息
         */
        Connection connection = configuration.getDataSource().getConnection();
        /**
         * 2.转化sql  select* from tb_user where id=#{id} and name=#{username}
         *   select * from tb_user where id=? and name=?
         */
        String sql=mappedStatement.getSql();
        /**
         * 处理sql
         *
         */
        BoundSql boundSql=getBoundSql(sql);
        String finalSql=boundSql.getSqlText();
        /**
         * 获取参数
         */
        Class<?> paramType= getClassType( mappedStatement.getParamterType());
        PreparedStatement preparedStatement=connection.prepareStatement(finalSql);
        //解析后的参数属性集合
        List<ParameterMapping> parameterMappingList=boundSql.getParameterMappingList();
        int paramListSize=parameterMappingList.size();
        ParameterMapping parameterMapping=null;
        for(int i=0;i<paramListSize;i++){
            parameterMapping=parameterMappingList.get(i);
            /**
             * 反射得到类的属性信息
             */
            String propertyName=parameterMapping.getContent();
            Field declaredField = paramType.getDeclaredField(propertyName);
            /**
             * 暴力设置属性被访问
             */
            declaredField.setAccessible(true);
            /**
             * 参数值
             */
            Object o=declaredField.get(params[0]);
            preparedStatement.setObject(i+1,o);
        }
        /**
         * 执行sql
         */
        return preparedStatement.executeUpdate();
    }

    private Class<?> getClassType(String  paramterType) throws ClassNotFoundException {
        if (null!=paramterType) {
            return Class.forName(paramterType);
        }
        return null;
    }

    /**
     * 完成sql语句的解析和转换
     * @param sql
     * @return
     */
    private BoundSql getBoundSql(String sql) {
        /**
         * 标记处理器，配置标记解析器来完成对站位符的解析处理
         */
        ParameterMappingTokenHandler parameterMappingTokenHandler=new ParameterMappingTokenHandler();
        /**
         * GenericTokenParser 通用的标记处理器，完成了代码里的站位符的解析，然后在根据给定的标记处理器（TokenHandler）来进行表达
         * 式的处理，并将占位符里的标识放入TokenHandler 里的 parameterMappingList 集合里
         * 参数如下： 1。站位符开始 2。占位符结束  3。标记处理器
         */
        String parseSql = new GenericTokenParser("#{","}",parameterMappingTokenHandler).parse(sql);
        List<ParameterMapping> parameterMappingList=parameterMappingTokenHandler.getParameterMappings();
        return new BoundSql(parseSql,parameterMappingList);
    }
    /***********下面代码废弃  暂时没时间*********************/
    /**
     * 获取链接
     * @return
     */
    public Connection connect(Configuration configuration) throws SQLException {

        return configuration.getDataSource().getConnection();
    }

    /**
     * 操作
     * @param sql
     * @param autoGeneratedKeys
     * @return
     */
    private PreparedStatement prepare(Configuration configuration,String sql, boolean autoGeneratedKeys) throws SQLException {
        Connection connection=connect(configuration);
        PreparedStatement ps = null;
        try {
            if (autoGeneratedKeys) {
                ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            } else {
                ps = connection.prepareStatement(sql);
            }
        } catch (SQLException e) {
            System.out.println("创建 PreparedStatement 对象失败: " + e.getMessage());
        }
        return ps;
    }
}